package org.example.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import org.example.util.ClassUtils;

import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class AutoImportModule extends AbstractModule {

    private final Set<Class<?>> sources;

    private final Map<Object, Set<Class<?>>> binderContext = new ConcurrentHashMap<>();


    public AutoImportModule(Class<?>... sources) {
        this.sources = new LinkedHashSet<>(Arrays.asList(sources));
    }

    @Override
    protected void configure() {
        sources.stream().flatMap(v -> new ClassPathClassNameScanner().doScan(v.getPackage().getName()).stream()).forEach(v -> {
            parseBinder(v);
            dynamicBinder();
        });
    }

    private void parseBinder(String className) {
        Class<?> beanClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
        if (null == beanClass.getAnnotation(Singleton.class) || Modifier.isAbstract(beanClass.getModifiers())) {
            return;
        }
        List<Class<?>> interfaceClasses = new ArrayList<>();
        findInterfaceClass(beanClass, interfaceClasses);
        Set<Class<?>> implClassSet = new HashSet<>();
        // 无接口绑定
        if (interfaceClasses.size() == 0 && null == binderContext.get(className)) {
            implClassSet.add(beanClass);
            binderContext.put(className, implClassSet);
            return;
        }
        // 多个接口对应多个实现
        for (Class<?> interfaceClass : interfaceClasses) {
            Set<Class<?>> existsClassSet = binderContext.get(interfaceClass);
            if (null == existsClassSet) {
                implClassSet.add(beanClass);
                binderContext.put(interfaceClass, implClassSet);
            } else {
                existsClassSet.add(beanClass);
            }
        }
    }

    @SuppressWarnings("all")
    private void dynamicBinder() {
        binderContext.forEach((k, v) -> {
            if (k instanceof String) {
                v.forEach(x -> bind(x).in(Singleton.class));
                return;
            }
            // 集合映射
            Multibinder multibinder = Multibinder.newSetBinder(binder(), (Class) k);
            v.forEach(x -> multibinder.addBinding().to(x).in(Singleton.class));
            // Map映射
            MapBinder<String, Class<?>> mapBinder = MapBinder.newMapBinder(binder(), String.class, (Class) k);
            v.forEach(x -> mapBinder.addBinding(ClassUtils.generateBeanName(((Class) x).getName())).to((Class) x).in(Singleton.class));
        });
    }

    /**
     * 查找该类的实现的所有接口
     *
     * @param beanClass
     * @param interfaceClasses
     */
    private void findInterfaceClass(Class<?> beanClass, List<Class<?>> interfaceClasses) {
        if (beanClass.isInterface()) {
            return;
        }
        Class<?>[] interfaces = beanClass.getInterfaces();
        if (interfaces.length > 0) {
            interfaceClasses.addAll(Arrays.asList(interfaces));
        }
        Class<?> superclass = beanClass.getSuperclass();
        if (!superclass.isAssignableFrom(Object.class)) {
            interfaceClasses.add(superclass);
            findInterfaceClass(superclass, interfaceClasses);
        }
    }
}
